#define MAX_ATTRDESC    29
#define ATTR_ID          0
#define ATTR_POS         1
#define ATTR_TEMP        2
#define ATTR_TAU         3
#define ATTR_HUM         4
#define ATTR_LIGHT       5
#define ATTR_RFID        6
#define ATTR_RFIDSIG     7
#define ATTR_BATT        8
#define ATTR_MEM         9
#define ATTR_TIME       10
#define ATTR_HOPS       11
#define ATTR_EXTRA0     12
#define ATTR_EXTRA1     13
#define ATTR_EXTRA2     14
#define ATTR_EXTRA3     15
#define ATTR_EXTRA4     16
#define ATTR_EXTRA5     17
#define ATTR_EXTRA6     18
#define ATTR_EXTRA7     19
#define ATTR_ADC0       20
#define ATTR_ADC1       21
#define ATTR_ADC2       22
#define ATTR_ADC3       23
#define ATTR_ADC4       24
#define ATTR_ADC5       25
#define ATTR_ADC6       26
#define ATTR_ADC7       27
#define ATTR_MAINTIMER  28

#define SAMPLE_TIMEOUT        25    // cycles to wait for result before sampling is termated with default result
#define SAMPLE_DEFAULTRESULT  0

#if defined (TOSSIM) || defined (UNIT_TEST)
char *attr_name[MAX_ATTRDESC] = {"id", "pos", "temp", "tau", "hum", "light", "rfid", "rfidsig", "batt",
  "mem", "time", "hops", "extra0", "extra1", "extra2", "extra3", "extra4", "extra5", "extra6", "extra7",
  "adc0", "adc1", "adc2", "adc3", "adc4", "adc5", "adc6", "adc7", "maintimer"};
#endif

#define MAX_ATTRUPDATENODES  10
#define MAX_SUMMARYDESC      10
#define ATTRQUEUE_SIZE       4

typedef struct {
  ATTRIBUTE value;
  uint32_t sampling_time, sample_begin, sample_number;
  uint8_t is_sampling, is_dynamic, is_hardware, needs_sampling, min_samples;
} PACKING_MACRO ATTR_DESC;

typedef struct {
  uint8_t attr_index;
  ATTRIBUTE value;
  uint8_t group_next;
  uint32_t sampling_time;
} PACKING_MACRO ATTRQUEUE_ENTRY;

#define ATTR_DYNAMICLIST   {ATTR_HUM, ATTR_LIGHT, ATTR_RFID, ATTR_RFIDSIG, ATTR_BATT, ATTR_MEM, ATTR_TIME, ATTR_ADC0, ATTR_ADC1, ATTR_ADC2, ATTR_ADC3}
#define ATTR_HARDWARELIST  {ATTR_TEMP, ATTR_HUM, ATTR_LIGHT, ATTR_BATT, ATTR_ADC0, ATTR_ADC1, ATTR_ADC2, ATTR_ADC3}
#define ATTR_SAMPLELIST    {ATTR_LIGHT, ATTR_ADC0, ATTR_ADC1, ATTR_ADC2, ATTR_ADC3, ATTR_TEMP, ATTR_BATT, ATTR_HUM}
#define ATTR_MINSAMPLELIST {{ATTR_LIGHT, 2}, {ATTR_ADC0, 1}, {ATTR_ADC1, 1}, {ATTR_ADC2, 1}, {ATTR_ADC3, 1}, {ATTR_TEMP, 4}}

typedef struct {
  uint8_t attrdesc_index;   // on which attribute is the summary
  uint8_t type;             // as defined in summary.h
  uint8_t options;          // as defined in summary.h
} PACKING_MACRO SUMMARY_DESC;

typedef struct {
  uint8_t cmd;

  uint8_t attr_index, node_count;
  struct {
    ADDRESS id;
    ATTRIBUTE value;
  } PACKING_MACRO nodes[MAX_ATTRUPDATENODES];
} PACKING_MACRO ATTRUPDATE_MSGHI;

typedef struct {
  uint8_t cmd;

  uint8_t attr_static[DIV_CEIL(MAX_ATTRDESC, 8)];
  uint8_t summarydesc_count;
  SUMMARY_DESC summary_desc[MAX_SUMMARYDESC];
} PACKING_MACRO SUMMARYSPEC_MSGHI;

#define TOKEN_OP      0
#define TOKEN_NUM     1
#define TOKEN_ATTR    2
#define TOKEN_BRACKET 3

#define OP_MUL         0
#define OP_DIV         1
#define OP_PLUS        2
#define OP_MINUS       3
#define OP_XOR         4
#define OP_EQ          5
#define OP_GR          6
#define OP_LESS        7
#define OP_GREQ        8
#define OP_LESSEQ      9
#define OP_AND        10
#define OP_OR         11
#define OP_NOT        12
#define OP_SHL        13
#define OP_SHR        14
#define OP_MOD        15
#define OP_DIST       16
#define OP_ABS        17
#define OP_HASH       18
#define OP_SQR        19
#define OP_SQRT       20
#define OP_IN         21
#define OP_IF         22
#define OP_AGGRCOUNT  23
#define OP_AGGRSUM    24
#define OP_AGGRMIN    25
#define OP_AGGRMAX    26
#define OP_COUNT      27

#if defined (TOSSIM) || defined (UNIT_TEST)
struct {
  char *name;
  int8_t priority;
  uint8_t arg_count;
  uint8_t id;
  uint8_t is_aggr, is_fn;
} ops[OP_COUNT] = {
  {"*", 2, 2, OP_MUL, 0, 0},       {"/", 2, 2, OP_DIV, 0, 0},       {"+", 1, 2, OP_PLUS, 0, 0},
  {"-", 1, 2, OP_MINUS, 0, 0},     {"^", 3, 2, OP_XOR, 0, 0},       {"=", 0, 2, OP_EQ, 0, 0},
  {">", 0, 2, OP_GR, 0, 0},        {"<", 0, 2, OP_LESS, 0, 0},      {">=", 0, 2, OP_GREQ, 0, 0},
  {"<=", 0, 2, OP_LESSEQ, 0, 0},   {"and", -1, 2, OP_AND, 0, 0},    {"or", -2, 2, OP_OR, 0, 0},
  {"not", 3, 1, OP_NOT, 0, 0},     {"<<", 0, 2, OP_SHL, 0, 0},      {">>", 0, 2, OP_SHR, 0, 0},
  {"%", 2, 2, OP_MOD, 0, 0},       {"dist", 2, 2, OP_DIST, 0, 1},   {"abs", 2, 1, OP_ABS, 0, 1},
  {"hash", 3, 1, OP_HASH, 0, 1},   {"sqr", 3, 1, OP_SQR, 0, 1},     {"sqrt", 3, 1, OP_SQRT, 0, 1},
  {"in", 2, 1, OP_IN, 0, 0},       {"if", 2, 3, OP_IF, 0, 1},       {"count", 4, 1, OP_AGGRCOUNT, 1, 1},
  {"sum", 4, 1, OP_AGGRSUM, 1, 1}, {"min", 4, 1, OP_AGGRMIN, 1, 1}, {"max", 4, 1, OP_AGGRMAX, 1, 1}};
#endif

typedef struct {
  uint8_t type;
  ATTRIBUTE value;
} PACKING_MACRO TOKEN;

#define OPGET_TYPE(v)              ((v) & 0xff)
#define OPGET_EXTRAARG(v)          ((v) >> 8)
#define OPSET(t, a)                ((t) | ((a) << 8))

#define ATTRGET_SEQ(v)             (((v) >> 9) & 0x01)
#define ATTRGET_REL(v)             (((v) >> 7) & 0x03)
#define ATTRGET_DYNAMIC(v)         (((v) >> 6) & 0x01)
#define ATTRGET_TEMP(v)            (((v) >> 5) & 0x01)
#define ATTRGET_TYPE(v)            ((v) & 0x1f)
#define ATTRSET(s, r, d, t, tp)    (((s) << 9) | ((r) << 7) | ((d) << 6) | ((t) << 5) | (tp))

typedef struct {
  uint8_t stack_len; 
  TOKEN *stack;
} PACKING_MACRO STACK;

typedef struct {
  STACK *s;
  uint8_t stack_ofs;
  ATTRIBUTE *stemp, *ttemp;
  uint16_t sample_tolerance;
  uint8_t need_sampling;
} PACKING_MACRO EVAL_INFO;

ATTR_DESC attr_desc[MAX_ATTRDESC];
SUMMARY_DESC summary_desc[MAX_SUMMARYDESC];
uint8_t summarydesc_count;

EVAL_INFO eval_info;

ATTRQUEUE_ENTRY attr_queue[ATTRQUEUE_SIZE];
uint8_t attrqueue_first, attrqueue_last, attrqueue_size;

void init_msensor();
uint8_t find_idsummary();
uint8_t find_repairsummary();

int32_t eval_stack(STACK *s, ATTRIBUTE *stemp, ATTRIBUTE *ttemp, uint16_t sample_tolerance);
uint8_t sensor_needsampling();

#ifdef TOSSIM
int sensor_offlinesample(uint32_t sample_time, ATTRIBUTE *value, uint8_t jsel);
#endif

void handle_attrupdatemsghi(ADDRESS source_node, ADDRESS dest_node, uint8_t path_nodecount,
  ADDRESS *path_nodes, uint8_t data_len, uint8_t *data);
void handle_summaryspecmsghi(ADDRESS source_node, ADDRESS dest_node, uint8_t path_nodecount,
  ADDRESS *path_nodes, uint8_t data_len, uint8_t *data);
uint8_t filterbaseflood_attrupdate(uint8_t *data_len, uint8_t *data);

void update_attr(uint8_t attr_index, ATTRIBUTE value, uint32_t sampling_time);
void sample_attr(uint8_t attr_index);

uint8_t stack_pop(STACK *s, TOKEN *t);

uint8_t attrqueue_push(uint8_t attr_index, ATTRIBUTE value, uint8_t group_next);
uint8_t attrqueue_pop();
uint8_t attrqueue_check(uint8_t *attr_mask);
